package sharefolder

import "C"
import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"github.com/containerd/btrfs"
	"github.com/gogf/gf/errors/gerror"
	"github.com/shirou/gopsutil/v3/disk"
	"gitlab.local/TerraMaster/tos-modules/core/tosutils"
	"gitlab.local/TerraMaster/tos-modules/database/sqlitemodel"
	"gitlab.local/TerraMaster/tos-modules/service/sharefolder"
	"gitlab.local/golibrary/utils"
	"io"
	"math"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var mvShareLog = filepath.Join(os.TempDir(), "mv_share.log")

type MvShareFolderPlan struct {
	Plan     float32 `json:"plan"`      //进度
	Speed    string  `json:"speed"`     //速度
	LeftTime int64   `json:"left_time"` //剩余时间(秒)
}

// MvShareFolder 移动共享文件夹
// path: 共享文件夹原路径
// toPath: 共享文件夹新路径
// ctx: 上下文
// 返回值: 0:成功 1:操作繁忙 2:容量不足 3:xfs不支持共享文件夹
func (f *Folder) MvShareFolder(path, volume, uname string, ginCtx context.Context) (int, error) {
	if f.mvShareFolderCancelFn != nil {
		return 1, nil
	}

	var data sqlitemodel.Share
	if err := f.dbService.DbGet().Model(&sqlitemodel.Share{}).Where("mntpath = ?", path).First(&data).Error; err != nil {
		return 0, gerror.Wrap(err, "mvShareFolder")
	}

	volume = filepath.Join("/", volume)
	oldFolderName := filepath.Base(path)
	folderName := oldFolderName
	if data.Ecryptfs == 1 {
		//xfs不支持共享文件夹
		dfs, err := disk.Partitions(false)
		if err != nil {
			return 0, gerror.Wrap(err, "mvShareFolder Partitions err")
		}
		for _, v := range dfs {
			if v.Mountpoint == volume {
				if v.Fstype == "xfs" {
					return 3, nil
				}
				break
			}
		}
		folderName = fmt.Sprintf("@%s@", folderName)
		//加密文件夹
		path = filepath.Join(filepath.Dir(path), fmt.Sprintf("@%s@", folderName))
	}

	//判断容量是否足够
	size, err := f.shareService.FolderSize(path, ginCtx)
	if err != nil {
		return 0, gerror.Wrap(err, "mvShareFolder2")
	}

	disk.Partitions(false)

	if f.shareService.VolumeUserQuota(volume, uname, size) {
		return 2, nil //容量不足
	}

	if data.Ecryptfs == 1 {
		//卸载加密文件夹
		if err = f.dirService.EcryptfsUmount(path); err != nil {
			return 0, gerror.Wrap(err, "mvShareFolder1")
		}
		go tosutils.ReStartBaseService("")
	}

	toPath := filepath.Join(volume, folderName)
	if !utils.Exists(toPath) {
		//创建btrfs子卷
		if err = btrfs.SubvolCreate(toPath); err != nil {
			//不为btrfs文件系统，使用普通文件夹
			_ = os.Mkdir(toPath, 0770)
		}
	}

	//继承acl权限
	_ = f.aclService.InheritedPermissions(path, toPath, false)

	ctx, cancelFunc := context.WithCancel(context.Background())
	f.mvShareFolderCancelFn = cancelFunc
	cmd := utils.ShellWithCmd(ctx, fmt.Sprintf("rsync -a --info=progress2 --remove-source-files --inplace %s/* %s", utils.StrSpecialLetters(path), utils.StrSpecialLetters(toPath)))
	f.logFile, err = os.Create(mvShareLog)
	stdErr := &bytes.Buffer{}
	cmd.Stdout = f.logFile
	cmd.Stderr = stdErr
	if err = cmd.Start(); err != nil {
		f.closeMvFn()
		return 0, gerror.Wrap(err, stdErr.String())
	}

	go func() {
		//等待命令执行完毕
		cmd.Wait()
		_ = f.shareData(oldFolderName, volume)
		_ = os.RemoveAll(path)
		f.closeMvFn()
	}()

	return 0, nil
}

func (f *Folder) shareData(shareName, volume string) error {
	columns := make(map[string]interface{})
	columns["mntpath"] = filepath.Join(volume, shareName)
	columns["device"] = filepath.Base(volume)
	if err := f.dbService.DbGet().Model(&sqlitemodel.Share{}).Where("foldername = ?", shareName).Updates(columns).Error; err != nil {
		return gerror.Wrap(err, "shareData")
	}
	tosutils.ReStartBaseService("")

	return nil
}

// MvShareFolderPlan 获取移动共享文件夹进度
// 返回值: 进度, 速度, 剩余时间(秒), 错误
func (f *Folder) MvShareFolderPlan() (*sharefolder.MvShareFolderPlan, error) {
	if !utils.Exists(mvShareLog) {
		return nil, nil
	}

	file, err := os.Open(mvShareLog)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	var line string
	scanner := bufio.NewReader(file)

	for {
		line, err = scanner.ReadString('\r')
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, gerror.Wrap(err, "mvShareFolderPlan")
		}
	}

	line = strings.TrimSpace(line)
	if line == "" {
		return nil, nil
	}
	fields := strings.Fields(line)
	if len(fields) < 3 {
		return nil, nil
	}

	res := &sharefolder.MvShareFolderPlan{}

	//获取进度
	plan, err := strconv.ParseFloat(strings.TrimSuffix(fields[1], "%"), 32)
	if err != nil {
		return nil, gerror.Wrap(err, "mvShareFolderPlan1")
	}
	res.Plan = float32(plan)
	if res.Plan == 100.00 {
		if f.mvShareFolderCancelFn != nil {
			res.Plan = 99.99
		}
		return res, nil
	}

	//获取速度
	res.Speed = fields[2]

	//获取剩余时间
	splitArr := strings.Split(fields[3], ":")
	if len(splitArr) < 2 {
		return res, nil
	}

	h, err := strconv.Atoi(splitArr[0])
	if err != nil {
		return nil, gerror.Wrap(err, "mvShareFolderPlan2")
	}
	i, err := strconv.Atoi(splitArr[1])
	if err != nil {
		return nil, gerror.Wrap(err, "mvShareFolderPlan3")
	}
	s, err := strconv.Atoi(splitArr[2])
	if err != nil {
		return nil, gerror.Wrap(err, "mvShareFolderPlan4")
	}
	leftTime := time.Duration(h)*time.Hour + time.Duration(i)*time.Minute + time.Duration(s)*time.Second
	res.LeftTime = int64(math.Ceil(leftTime.Seconds()))

	return res, nil
}

func (f *Folder) closeMvFn() {
	if f.mvShareFolderCancelFn != nil {
		f.mvShareFolderCancelFn()
		f.mvShareFolderCancelFn = nil
	}
	if f.logFile != nil {
		_ = f.logFile.Close()
		f.logFile = nil
	}
}

func (f *Folder) ShareFolderListByVolume(v, uname string) (interface{}, error) {
	strList := make([]string, 0)
	filter, err := f.folders.GetHomeListFilter(uname)
	if err != nil {
		return nil, err
	}
	//过滤权限
	for _, folder := range filter {
		if folder.Volume == v && folder.Name != "homes" && folder.Name != uname {
			strList = append(strList, folder.Path)
		}
	}
	return strList, nil
}
